Pektis.prototype.pektisRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Peparam.prototype.peparamRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Profinfo.prototype.profinfoRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Sxesi.prototype.sxesiRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Trapezi.prototype.trapeziRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Trparam.prototype.trparamRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Prosklisi.prototype.prosklisiRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Partida.prototype.partidaRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Energia.prototype.energiaRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Sinedria.prototype.sinedriaRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

Sizitisi.prototype.sizitisiRowSet = function(row) {
	globals.initObject(this, row);
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Pektis.prototype.klidomaPektis = function(conn, opts) {
	var query, pektis = this;

	if (opts === undefined) opts = {};
	if (!opts.hasOwnProperty('timeout')) opts.timeout = 2;
	if (!opts.hasOwnProperty('onsuccess')) opts.onsuccess = function(conn) {
		conn.free();
	};
	if (!opts.hasOwnProperty('onerror')) opts.onerror = function(conn) {
		conn.free();
	};

	query = 'SELECT GET_LOCK(' + this.klidomaPektisTag() + ', ' + opts.timeout + ') AS `lock`';
	conn.query(query, function(conn, rows) {
		if (rows.length != 1) opts.onerror(conn, pektis);
		else if (rows[0]['lock'] == 1) opts.onsuccess(conn, pektis);
		else opts.onerror(conn, pektis);
	});

	return this;
}

Pektis.prototype.xeklidomaPektis = function(conn, callback) {
	var query, pektis = this;

	if (callback === undefined) callback = function(conn) {
		conn.free();
	}

	query = 'DO RELEASE_LOCK(' + this.klidomaPektisTag() + ')';
	conn.query(query, function(conn) {
		callback(conn, pektis);
	});

	return this;
}

Pektis.prototype.klidomaPektisTag = function() {
	return globals.json('pektis:' + this.login);
}

Pektis.prototype.pektisIncognito = function() {
	delete this.kodikos;
	delete this.email;

	return this;
}

Sinedria.prototype.sinedriaIncognito = function() {
	delete this.klidi;
	delete this.ip;
	delete this.isodos;
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η μέθοδος "pektisLoad" μαζί με τις παρελκόμενες χρησιμοποιούνται για την ανανέωση των
// στοιχείων του παίκτη από την database. Ως παραμέτρους περνάμε μια callback function
// που καλείται μετά τον επιτυχή εντοπισμό του παίκτη και δεύτερη callback που καλείται
// όταν ο παίκτης δεν υπάρχει στην database. Και οι δύο callback functions καλούνται με
// παράμετρο τον ίδιο τον ανά χείρας παίκτη. Για τη σύνδεση με την database δημιουργείται
// εσωτερική database connection, εκτός και αν περάσουμε άλλη database connection ως τρίτη
// παράμετρο.

Pektis.prototype.pektisLoad = function(success, failure, conn) {
	var conn, aftonomi = true, query, pektis = this;

	if ((failure === undefined) || (failure === null)) failure = function(pektis) {
		globals.fatal(pektis.login + ': δεν βρέθηκε ο παίκτης στην database');
	}

	if (conn === undefined) conn = DB.connection();
	else aftonomi = false;

	query = 'SELECT UNIX_TIMESTAMP(`egrafi`) AS `egrafi`, `onoma`, `email`, ' +
		'UNIX_TIMESTAMP(`poll`) AS `poll` FROM `pektis` ' +
		'WHERE (`login` = BINARY ' + conn.escape(this.login) + ')';
	if (this.hasOwnProperty('kodikos')) query += ' AND (`kodikos` = BINARY ' +
		conn.escape(this.kodikos) + ')';
	conn.query(query, function(conn, rows) {
		if (rows.length != 1) {
			if (aftonomi) conn.free();
			failure(pektis);
			return;
		}

		pektis.pektisRowSet(rows[0]);
		pektis.pektisLoadPeparam(conn, success, aftonomi);
	});

	return this;
}

Pektis.prototype.pektisLoadPeparam = function(conn, success, aftonomi) {
	var query, pektis = this;

	this.peparam = {};
	query = 'SELECT `param`, `timi` FROM `peparam` WHERE `pektis` LIKE ' + conn.escape(this.login);
	conn.query(query, function(conn, rows) {
		var i;
		for (i = 0; i < rows.length; i++) {
			pektis.peparamSet(new Peparam().peparamRowSet(rows[i]));
		}

		pektis.pektisLoadProfinfo(conn, success, aftonomi);
	});
}

Pektis.prototype.pektisLoadProfinfo = function(conn, success, aftonomi) {
	var query, pektis = this;

	this.profinfo = {};
	query = 'SELECT `sxoliastis`, `kimeno` FROM `profinfo` WHERE `pektis` LIKE ' + conn.escape(this.login);
	conn.query(query, function(conn, rows) {
		var i;
		for (i = 0; i < rows.length; i++) {
			pektis.profinfoSet(new Profinfo().profinfoRowSet(rows[i]));
		}

		pektis.pektisLoadSxesi(conn, success, aftonomi);
	});
}

Pektis.prototype.pektisLoadSxesi = function(conn, success, aftonomi) {
	var query, pektis = this;

	this.sxesi = {};
	query = 'SELECT `sxetizomenos`, `sxesi` FROM `sxesi` WHERE `pektis` LIKE ' + conn.escape(this.login);
	conn.query(query, function(conn, rows) {
		var i;
		if (aftonomi) conn.free();
		for (i = 0; i < rows.length; i++) {
			pektis.sxesiSet(new Sxesi().sxesiRowSet(rows[i]));
		}

		if (success) success(pektis);
	});
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η μέθοδος "sinedriaArxio" επιχειρεί να αρχειοθετήσει τυχόν συνεδρία του παίκτη
// και κατόπιν να διαγράψει τη συνεδρία. Υπενθυμίζουμε ότι οι συνεδρίες φέρουν ως
// primary key το login name του παίκτη και ότι αρχειοθέτηση της συνεδρίας είναι
// η μεταφορά της συνεδρίας στον πίνακα "istoriko".
//
// Η μέθοδος δέχεται μια database connection στα πλαίσια της οποίας θα γίνουν
// οι αλλαγές στην database, ενώ ως δεύτερη και τρίτη παράμετρο δέχεται callback
// functions που θα κληθούν με παραμέτρους την database connection και τον ανά
// χείρας παίκτη μετά την επιτυχή ή την ατυχή έκβαση της διαδικασίας. Αυτές οι
// functions θα κληθούν ως μέθοδοι του παίκτη με παράμετρο την database connection.

Pektis.prototype.sinedriaArxio = function(conn, onsuccess, onerror) {
	var pektis = this,
	query = 'INSERT INTO `istoriko` (`pektis`, `ip`, `isodos`, `exodos`) ' +
		'SELECT `pektis`, `ip`, `isodos`, NOW() FROM `sinedria` ' +
		'WHERE (`pektis` = BINARY ' + globals.json(this.login) + ')';

	conn.query(query, function(conn, res) {
		var
		cnt = conn.affectedRows,
		query = 'DELETE FROM `sinedria` WHERE `pektis` = BINARY ' + globals.json(pektis.login);

		conn.query(query, function(conn) {
			if (conn.affectedRows == cnt) onsuccess(conn, pektis);
			else onerror(conn, pektis);
		});
	});
}

Sinedria.prototype.pollSet = function() {
	this.poll = globals.tora();
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η κίνηση περιέχει ως property μια JSON αναπαράσταση του ίδιου του εαυτού της προκειμένου
// να την αποστέλλει στους clients κατά τη φάση της ενημέρωσης. Αυτή η property είναι η "=",
// ενώ παρόμοια είναι η property "~" που περιέχει JSON αναπαράσταση της κινήσεως μετά από
// προσαρμογή σχετική με την προστασία των προσωπικών δεδομένων. Για να δημιουργήσουμε την
// συγκεκριμένη property φροντίζουμε, λοιπόν, πρώτα να διαγράψουμε τυχόν υπάρχουσες τέτοιου
// είδους παράγωγες properties.

Kinisi.prototype.aftousiaSet = function() {
	return this.delProp('=').delProp('~').set('=', JSON.stringify(this));
}

Kinisi.prototype.aftousiaGet = function() {
	return this.get('=');
}

// Όπως αναφέραμε παραπάνω, η property "~" περιέχει JSON αναπαράσταση της κίνησης μετά από τυχόν
// προσαρμογή που αφορά σε προστασία προσωπικών δεδομένων της κίνησης. Επειδή στις περισσότερες
// περιπτώσεις η προσαρμοσμένη κίνηση είναι η ίδια για όλους τους μη οικείους clients, κρατάμε
// την JSON μορφή της στο property "~". Στις περιπτώσεις που η προσαρμογή είναι ανάλογη με τον
// client, η property αυτή δεν χρησιμοποιείται κατ' επανάληψη αλλά δημιουργείται εκ νένου για
// κάθε client ξεχωριστά.

Kinisi.prototype.prosarmosmeniSet = function(s) {
	this.set('~', s);
	return this;
}

Kinisi.prototype.prosarmosmeniGet = function() {
	return this.get('~');
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Skiniko.prototype.kinisiAdd = function(kinisi, enimerosi) {
	this.kinisi.push(kinisi.aftousiaSet());
	this.kinisiMazema();

	if (enimerosi === undefined) enimerosi = true;
	if (enimerosi) this.enimerosi();

	return this;
}

Skiniko.prototype.kinisiGet = function(i) {
	return this.kinisi[i];
}

Skiniko.prototype.kinisiWalk = function(callback) {
	var i, klen = this.kinisiPlithos();

	for (i = 0; i < klen; i++) {
		callback(i, this.kinisiGet(i));
	}

	return this;
}

Skiniko.prototype.enimerosi = function() {
	var skiniko = this;

	this.sinedriaWalk(function(login, sinedria) {
		sinedria.enimerosi(skiniko);
	});

	return this;
}

Skiniko.prototype.feredataClose = function(login) {
	var sinedria = this.sinedriaGet(login);
	if (sinedria) sinedria.feredataClose();
	return this;
}

Sinedria.prototype.enimerosi = function(skiniko) {
	var feredata, klen, floter, kinisi, idos, func, sent;

	feredata = this.feredataGet();
	if (!feredata) return this;

	klen = skiniko.kinisiPlithos();
	floter = this.floterGet();
	if (floter === undefined) return feredata.end('"?":"απροσδιόριστο φλοτέρ κινήσεων συνεδρίας"');
	if (floter >= klen) return this;

	feredata.write('k:[');
	for (sent = 0; floter < klen; floter++) {
		kinisi = skiniko.kinisiGet(floter);
		idos = kinisi.idosGet();

		func = 'adiaforiKinisi' + idos;
		if (this[func] && this[func](kinisi)) continue;

		func = 'prosarmogiKinisis' + idos;
		if (this[func] && this[func](kinisi)) feredata.write(kinisi.prosarmosmeniGet());
		else feredata.write(kinisi.aftousiaGet());

		feredata.write(',');
		sent++;
	}
	feredata.write('],');

	klen = skiniko.kinisiPlithos();
	if (floter != klen) {
		console.log('WARNING: transaction log length: ' + klen + ' <> ' + floter);
		return feredata.end('"?":"transaction log infection"');
	}

	this.floterSet(klen);
	if (sent) {
		feredata.end();
		delete Feredata[this.pektis];
	}

	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Sinedria.prototype.feredataClose = function(feredata, rsp) {
	var feredata = this.feredataGet();
	if (!feredata) return this;

	feredata.end(rsp === undefined ? '"~":0' : rsp);
	delete Feredata[this.pektis];
	return this;
}

// Για να αποφύγουμε κυκλικά αδιέξοδα δεν κρατάμε τα κανάλια επικοινωνίας μέσα
// στις ίδες τις ίδιες τις συνεδρίες στις οποίες αφορούν, αλλά σε εξωτερική λίστα
// δεικτοδοτημένη με το login name τού παίκτη.

Feredata = {};

Sinedria.prototype.feredataSet = function(nodereq) {
	this.feredataClose();
	Feredata[this.pektis] = nodereq;
	this.pollSet();
	return this;
}

Sinedria.prototype.feredataGet = function() {
	return Feredata[this.pektis];
}

Sinedria.prototype.oxiFeredata = function() {
	return !this.feredataGet();
}

Sinedria.prototype.floterSet = function(len) {
	this.floter = len;
	return this;
}

Sinedria.prototype.floterGet = function() {
	return this.floter;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Skiniko.prototype.kinisiReset = function() {
	Log.print('resetting the transaction log');
	this.kinisi = [];
	this.kinisiMax = 1000;
	this.kinisiAfxisi = 500;
	this.kinisMaxEpitrepto = 10000;
	return this;
}

Skiniko.prototype.kinisiPlithos = function() {
	return this.kinisi.length;
}

Skiniko.prototype.kinisiMazema = function() {
	var klen = this.kinisiPlithos(), pektis, sinedria, floter, min = null;

	if (klen < this.kinisiMax) return this;

	console.log('shrinking the transaction log (' + klen + ')');
	for (pektis in this.sinedria) {
		sinedria = this.sinedriaGet(pektis);
		if (sinedria.oxiFeredata()) continue;

		floter = sinedria.floterGet();
		if ((min === null) || (floter < min)) min = floter;
		if (min) continue;

		// Η ελάχιστη στάθμη κάποιας συνεδρίας είναι μηδενική, επομένως δεν μπορούμε να
		// προβούμε σε μείωση του transaction log αυτή τη στιγμή και προκειμένου να μην
		// μπαίνουμε συνεχώς σε αυτή τη διαδικασία, αυξάνουμε το μέγιστο επιτρεπτό μήκος
		// του transaction log και διακόπτουμε τη διαδικασία.

		console.log('raising the transaction log size (' + this.kinisiMax + ')');
		this.kinisiMax += this.kinisiAfxisi;
		if (this.kinisiMax <= this.kinisiMaxEpitrepto) return this;
		globals.fatal('transaction log max size exceeded!');
	}

	for (pektis in this.sinedria) {
		sinedria = this.sinedriaGet(pektis);
		if (sinedria.oxiFeredata()) continue;

		floter = sinedria.floterGet();
		sinedria.floterSet(floter - min);
	}

	this.kinisi.splice(0, min);
	console.log('transaction log shrinked (' + this.kinisiPlithos() + ')');
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Pektis.prototype.pektisProsarmogi = function(endiaferomenos) {
	var login = this.loginGet(), pektis;
	if (login == endiaferomenos) return this;

	pektis = this;
	delete this.sxesi;
	this.profinfoWalk(function(sxoliastis, profinfo) {
		if (sxoliastis == login) return;
		if (sxoliastis == endiaferomenos) return;
		delete pektis.profinfo[sxoliastis];
	});

	return this;
}

// Η μέθοδος "trapeziRowAdd" επιχειρεί να εισαγάγει το ανά χείρας τραπέζι στην database.
// Ως πρώτη παράμετρο δέχεται μια database connection. Ακολουθούν δύο functions που θα κληθούν
// ως μέθοδοι του τραπεζιού σε περίπτωση επιτυχίας ή αποτυχίας της εισαγωγής. Αυτές οι μέθδοι
// θα κληθούν με παράμετρο την database connection.

Trapezi.prototype.trapeziRowAdd = function(conn, onsuccess, onfailure) {
	var
	thesi, col,
	trapezi = this,
	kodikosAuto = true,
	tora = globals.toraServer(),
	sep = '',
	query1 = 'INSERT INTO `trapezi` (',
	query2 = ') VALUES (',
	query3 = ')';

	if (this.hasOwnProperty('kodikos')) {
		kodikosAuto = false;
		query1 += sep + '`kodikos`';
		query2 += sep + this.kodikosGet();
		sep = ', ';
	}

	if (!this.hasOwnProperty('stisimo')) this.stisimoSet(tora);
	query1 += sep + '`stisimo`';
	query2 += sep + this.stisimoGet();
	sep = ', ';

	geister.thesiWalk(function(thesi) {
		col = 'pektis' + thesi;
		if (trapezi.hasOwnProperty(col)) {
			query1 += sep + '`' + col + '`';
			query2 += sep + globals.json(trapezi.pektisGet(thesi));
			sep = ', ';
		}

		col = 'apodoxi' + thesi;
		if (!trapezi.hasOwnProperty(col)) trapezi.apodoxiSet('ΟΧΙ', thesi);
		query1 += sep + '`' + col + '`';
		query2 += sep + globals.json(trapezi.apodoxiGet(thesi));
		sep = ', ';
	});

	if (!this.hasOwnProperty('poll')) this.pollSet(tora);
	query1 += sep + '`poll`';
	query2 += sep + this.pollGet();
	sep = ', ';

	if (this.hasOwnProperty('arxio')) {
		query1 += sep + '`arxio`';
		query2 += sep + this.arxioGet();
		sep = ', ';
	}

	conn.query(query1 + query2 + query3, function(conn) {
		if (conn.affectedRows == 1) {
			if (kodikosAuto) trapezi.kodikosSet(conn.insertId);
			if (onsuccess) onsuccess.call(trapezi, conn);
			else conn.free();
		}
		else {
			if (onfailure) onfailure.call(trapezi, conn);
			else conn.free();
		}
	});

	return this;
}

// Η μέθοδος "prosklisiRowAdd" επιχειρεί να εισαγάγει την ανά χείρας πρόσκληση στην database.
// Ως πρώτη παράμετρο δέχεται μια database connection. Ακολουθούν δύο functions που θα κληθούν
// ως μέθοδοι της προσκλήσεως σε περίπτωση επιτυχίας ή αποτυχίας της εισαγωγής. Αυτές οι μέθδοι
// θα κληθούν με παράμετρο την database connection.

Prosklisi.prototype.prosklisiRowAdd = function(conn, onsuccess, onfailure) {
	var
	prosklisi = this,
	kodikosAuto = true,
	sep = '',
	query1 = 'INSERT INTO `prosklisi` (',
	query2 = ') VALUES (',
	query3 = ')';

	if (this.hasOwnProperty('kodikos')) {
		kodikosAuto = false;
		query1 += sep + '`kodikos`';
		query2 += sep + globals.json(this.kodikosGet());
		sep = ', ';
	}

	if (this.hasOwnProperty('trapezi')) {
		query1 += sep + '`trapezi`';
		query2 += sep + this.trapeziGet();
		sep = ', ';
	}

	if (this.hasOwnProperty('apo')) {
		query1 += sep + '`apo`';
		query2 += sep + globals.json(this.apoGet());
		sep = ', ';
	}

	if (!this.hasOwnProperty('pros')) this.prosSet(this.apoGet());
	query1 += sep + '`pros`';
	query2 += sep + globals.json(this.prosGet());
	sep = ', ';

	if (!this.hasOwnProperty('epidosi')) this.epidosiSet(globals.toraServer());
	query1 += sep + '`epidosi`';
	query2 += sep + this.epidosiGet();
	sep = ', ';

	conn.query(query1 + query2 + query3, function(conn) {
		if (conn.affectedRows == 1) {
			if (kodikosAuto) prosklisi.kodikosSet(conn.insertId);
			if (onsuccess) onsuccess.call(prosklisi, conn);
			else conn.free();
		}
		else {
			prosklisi.kodikosSet(conn.insertId);
			if (onfailure) onfailure.call(prosklisi, conn);
			else conn.free();
		}
	});

	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η μέθοδος "telosSetRow" ενημερώνει το τέλος της παρτίδας στην database. Ως παραμέτρους
// περνάμε μια database connection, τη θέση του νικητή, μια callback που θα κληθεί σε
// περίπτωση επιτυχίας και μια callback που θα κληθεί σε περίπτωση αποτυχίας. Οι callback
// function καλούνται ως μέθοδοι της παρτίδας με παραμέτρους την database connection και
// τη θέση του νικητή.

Partida.prototype.telosSetRow = function(conn, nikitis, onsuccess, onerror) {
	var partida = this, query;

 	query = 'UPDATE `partida` SET `nikitis` = ' + nikitis +
		', `telos` = NOW() WHERE `kodikos` = ' + this.kodikosGet();
	conn.queryRaw(query, function(err, res) {
		if (res && (res.affectedRows == 1)) onsuccess.call(partida, conn, nikitis);
		else onerror.call(partida, conn, nikitis);
	});

	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Energia.prototype.insertRow = function(conn, onsuccess, onerror) {
	var energia = this, query;

	if (!this.hasOwnProperty('data')) this.dataSet('');
 	query = 'INSERT INTO `energia` (`partida`, `pektis`, `idos`, `data`) VALUES (' +
		this.partidaGet() + ', ' + this.pektisGet() + ', ' + globals.json(this.idosGet()) + ', ' +
		globals.json(this.dataGet()) + ')';
	conn.queryRaw(query, function(err, res) {
		if ((!res) || (res.affectedRows != 1)) return onerror.call(energia, conn);

		energia.kodikosSet(res.insertId);
		onsuccess.call(energia, conn);
	});
}
